package org.example.backend.util;

import org.mindrot.jbcrypt.BCrypt;

/**
 * BCrypt 密码哈希工具类
 *
 * 功能：
 * 1. 密码哈希处理
 * 2. 密码验证
 * 3. 工作因子配置
 * 4. 哈希有效性检查
 */
public class BCryptUtils {

    // 默认工作因子 (2^10 = 1024 轮哈希计算)
    private static final int DEFAULT_COST = 10;

    // 最小工作因子
    private static final int MIN_COST = 4;

    // 最大工作因子
    private static final int MAX_COST = 31;

    /**
     * 私有构造方法，防止实例化
     */
    private BCryptUtils() {
        throw new IllegalStateException("工具类不应被实例化");
    }

    /**
     * 使用默认工作因子生成BCrypt哈希
     *
     * @param plainPassword 明文密码
     * @return 哈希后的密码
     * @throws IllegalArgumentException 如果密码为空
     */
    public static String hash(String plainPassword) {
        return hash(plainPassword, DEFAULT_COST);
    }

    /**
     * 使用指定工作因子生成BCrypt哈希
     *
     * @param plainPassword 明文密码
     * @param cost 工作因子 (4-31)
     * @return 哈希后的密码
     * @throws IllegalArgumentException 如果密码为空或工作因子无效
     */
    public static String hash(String plainPassword, int cost) {
        if (plainPassword == null || plainPassword.isEmpty()) {
            throw new IllegalArgumentException("密码不能为空");
        }

        validateCostFactor(cost);

        // 生成盐值并哈希密码
        String salt = BCrypt.gensalt(cost);
        return BCrypt.hashpw(plainPassword, salt);
    }

    /**
     * 验证明文密码与哈希是否匹配
     *
     * @param plainPassword 待验证的明文密码
     * @param hashedPassword 存储的哈希密码
     * @return 是否匹配
     * @throws IllegalArgumentException 如果任一参数为空或哈希格式无效
     */
    public static boolean verify(String plainPassword, String hashedPassword) {
        if (plainPassword == null || plainPassword.isEmpty()) {
            throw new IllegalArgumentException("待验证密码不能为空");
        }

        if (hashedPassword == null || hashedPassword.isEmpty()) {
            throw new IllegalArgumentException("哈希密码不能为空");
        }

        if (!isValidBCryptHash(hashedPassword)) {
            throw new IllegalArgumentException("无效的BCrypt哈希格式");
        }

        return BCrypt.checkpw(plainPassword, hashedPassword);
    }

    /**
     * 检查字符串是否是有效的BCrypt哈希
     *
     * @param hashedPassword 待检查的哈希字符串
     * @return 是否是有效的BCrypt哈希
     */
    public static boolean isValidBCryptHash(String hashedPassword) {
        if (hashedPassword == null) {
            return false;
        }

        // BCrypt哈希格式: $2a$10$N9qo8uLOickgx2ZMRZoMy.MIjF7wYdXpQYT.1ZR5UJD9XaO1QrLbK
        return hashedPassword.matches("^\\$2[ayb]\\$\\d{2}\\$[./0-9A-Za-z]{53}$");
    }

    /**
     * 从哈希中提取工作因子
     *
     * @param hashedPassword BCrypt哈希
     * @return 工作因子
     * @throws IllegalArgumentException 如果哈希格式无效
     */
    public static int extractCostFactor(String hashedPassword) {
        if (!isValidBCryptHash(hashedPassword)) {
            throw new IllegalArgumentException("无效的BCrypt哈希格式");
        }

        // 提取$后的两位数字
        String costStr = hashedPassword.substring(4, 6);
        return Integer.parseInt(costStr);
    }

    /**
     * 验证工作因子是否在有效范围内
     *
     * @param cost 工作因子
     * @throws IllegalArgumentException 如果工作因子无效
     */
    private static void validateCostFactor(int cost) {
        if (cost < MIN_COST || cost > MAX_COST) {
            throw new IllegalArgumentException(
                    String.format("工作因子必须在%d-%d之间", MIN_COST, MAX_COST));
        }
    }

    /**
     * 升级哈希密码的工作因子
     *
     * @param plainPassword 明文密码
     * @param hashedPassword 旧的哈希密码
     * @param newCost 新的工作因子
     * @return 新的哈希密码，如果验证失败返回null
     * @throws IllegalArgumentException 如果参数无效
     */
    public static String upgradeHash(String plainPassword, String hashedPassword, int newCost) {
        if (!verify(plainPassword, hashedPassword)) {
            return null;
        }

        validateCostFactor(newCost);
        int currentCost = extractCostFactor(hashedPassword);

        if (newCost <= currentCost) {
            // 不需要升级
            return hashedPassword;
        }

        return hash(plainPassword, newCost);
    }
}